Skip to content

feat: create @launchdarkly/js-contract-test-utils package#1163

Merged
joker23 merged 14 commits into
mainfrom
devin/SDK-2008-1773078228
Mar 10, 2026
Merged

feat: create @launchdarkly/js-contract-test-utils package#1163
joker23 merged 14 commits into
mainfrom
devin/SDK-2008-1773078228

Conversation

@joker23

@joker23 joker23 commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

BEGIN_COMMIT_OVERRIDE
ci: consolidate contract test utils to @launchdarkly/js-contract-test-utils package
END_COMMIT_OVERRIDE

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Related issues

  • SDK-2008
  • Reference branch: devin/SDK-1866-1772645970

Describe the solution you've provided

Creates a new private package @launchdarkly/js-contract-test-utils at packages/tooling/contract-test-utils/ that consolidates duplicated contract test infrastructure shared across client-side SDKs, and migrates browser, electron, react-native, and React SDK contract tests to consume it.

Package exports

Import Path Contents Resolution
. Universal types (CommandParams, ConfigParams), makeLogger, ClientPool Source .ts
./client Re-exports universal + client-side TestHook (fetch-based) Source .ts

All types import LDContext/LDEvaluationReason/LDLogger from @launchdarkly/js-client-sdk-common for cross-SDK compatibility.

Migrations included

For each of browser, electron, react-native, and React contract test entities:

  • Deleted local CommandParams.ts, ConfigParams.ts, makeLogger.ts, TestHook.ts
  • Updated consuming files (ClientEntity.ts, ClientFactory.ts, TestHarnessWebSocket.ts, ClientRoot.tsx as applicable) to import from @launchdarkly/js-contract-test-utils/client
  • Added "@launchdarkly/js-contract-test-utils": "workspace:^" dependency

Review feedback addressed

  • Converted CommandType, ValueType, HookStage from TypeScript enum to as const objects with derived union types per repo guidelines
  • Fixed browser entity dependency versions from "*" to "workspace:^"
  • Consolidated duplicate HookStage definition (was in both CommandParams.ts and ConfigParams.ts, now only in CommandParams.ts)
  • Removed .js extensions from shared package internal imports for Next.js Turbopack compatibility (React SDK uses next build with Turbopack, which resolves from .ts source and can't find .js targets)
  • Enabled unstable_enablePackageExports in react-native entity's metro.config.js so Metro can resolve the ./client subpath export
  • Fixed ClientPool.nextId() off-by-one: now returns current counter value before incrementing, so the first ID is "0" (matching existing consumer patterns)
  • Simplified ClientPool.add() API: now calls nextId() internally and returns the assigned ID (add(client: T): string instead of requiring the caller to pass an ID)

Human review checklist

  • README example is stale: packages/tooling/contract-test-utils/README.md shows old ClientPool usage (pool.nextId() then pool.add(id, entity)) — should be updated to use the new pool.add(entity) API that returns the ID
  • as const type compatibility: Verify CommandType, ValueType, and HookStage union types derived from as const objects are assignable everywhere the old enum/type union was used (especially in switch statements and type narrowing)
  • Metro unstable_enablePackageExports: Confirm this doesn't cause unexpected resolution changes for other dependencies in the react-native entity

Items for reviewer attention

  1. No unit tests — This is test infrastructure itself (test coverage checkbox unchecked). Confirm whether tests are expected at this stage.
  2. Source-only resolution — All exports resolve to .ts source files (no compiled dist/). Internal imports use extensionless paths for compatibility with both bundlers (Vite, esbuild, Metro) and Next.js Turbopack. If a future consumer requires compiled ESM output, .js extensions would need to be added back (per Node.js ESM spec), but current consumers work correctly with source-only resolution.
  3. Server-side migration not included — Server-node and Shopify Oxygen contract tests are not migrated in this PR (deferred to follow-up per earlier discussion). The ClientPool utility is exported but not yet consumed.
  4. Metro bundler configuration — React Native's Metro bundler doesn't support package.json exports field by default. Enabled unstable_enablePackageExports in the react-native entity's metro.config.js to resolve @launchdarkly/js-contract-test-utils/client. This flag is widely supported in Metro 0.81+ but remains marked "unstable" - please verify it doesn't cause unexpected issues with other package resolutions.

Describe alternatives you've considered

  • Considered using plain string union types (type CommandType = 'evaluate' | 'evaluateAll' | ...) but this would break the CommandType.EvaluateFlag access pattern used throughout all consumer files. The as const object pattern preserves both the named access and the string union type.
  • Considered importing from SDK-specific packages (e.g. @launchdarkly/electron-client-sdk) but this would create circular dependencies. Using @launchdarkly/js-client-sdk-common keeps the shared package independent.
  • Considered keeping .js extensions in imports (per Node.js ESM spec) but Turbopack cannot resolve these when consuming TypeScript source. Extensionless imports work for all current consumers (bundlers + Turbopack) and are acceptable since the package is private and source-only.
  • Considered adding fallback main field to package.json for Metro compatibility, but enabling unstable_enablePackageExports is the recommended approach and cleaner long-term.

Additional context

  • All 4 migrated SDK contract tests compile and pass lint/prettier checks
  • Package uses workspace protocol (workspace:^) for monorepo dependencies
  • No bundle size changes (contract test code is not included in SDK bundles)
  • All 43/43 CI checks pass

Updates since last revision

  • Migrated React SDK contract tests (deleted 4 local files, updated imports in ClientEntity.ts, ClientRoot.tsx, TestHarnessWebSocket.ts)
  • Fixed Turbopack module resolution by removing .js extensions from shared package internal imports
  • Fixed Metro bundler resolution for react-native by enabling unstable_enablePackageExports in metro.config.js
  • Fixed ClientPool.nextId() off-by-one bug to match existing consumer patterns (first ID is now "0" instead of "1")
  • Simplified ClientPool.add() to internally call nextId() and return the assigned ID (API changed from add(id: string, client: T): void to add(client: T): string)
  • All CI checks now pass (43/43)

Link to Devin Session: https://app.devin.ai/sessions/f541db4796ce485a892086b8ea79bf95
Requested by: @joker23


Note

Medium Risk
Touches multiple contract-test runners and introduces package exports/Metro resolution changes; low production impact but moderate risk of breaking test builds across platforms due to module-resolution and shared type changes.

Overview
Introduces a new private workspace package, @launchdarkly/js-contract-test-utils, to centralize shared contract-test infrastructure (common command/config types, makeLogger, a fetch-based client-side TestHook, and a generic ClientPool), using @launchdarkly/js-client-sdk-common for cross-SDK type compatibility and exposing a ./client subpath export.

Migrates the browser, Electron, React Native, and React contract-test entities to consume these shared exports, deleting their duplicated local type/logger/hook files, updating imports/dependencies accordingly, and enabling Metro unstable_enablePackageExports so React Native can resolve the ./client subpath.

Written by Cursor Bugbot for commit 2ab4f54. This will update automatically on new commits. Configure here.

Create a new shared package at packages/tooling/contract-test-utils/ that
consolidates duplicated contract test code across browser, React Native,
Electron, server-node, and Shopify Oxygen SDKs.

The package provides:
- Universal types (CommandParams, ConfigParams) with no SDK dependency
- Client-side utilities (TestHook, TestHarnessWebSocket) for browser-like envs
- Server-side utilities (TestHook, ClientPool) for Node.js environments
- REST-to-WebSocket adapter bridge for browser/RN contract tests
- CLI executable (sdk-testharness-server / sts) with config file support
- Subpath exports organized by platform (., ./client, ./server, ./adapter)

This is a private package used only within the monorepo.

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

@launchdarkly/js-sdk-common size report
This is the brotli compressed size of the ESM build.
Compressed size: 25566 bytes
Compressed size limit: 26000
Uncompressed size: 125383 bytes

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

@launchdarkly/browser size report
This is the brotli compressed size of the ESM build.
Compressed size: 172130 bytes
Compressed size limit: 200000
Uncompressed size: 800872 bytes

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

@launchdarkly/js-client-sdk-common size report
This is the brotli compressed size of the ESM build.
Compressed size: 21281 bytes
Compressed size limit: 24000
Uncompressed size: 110213 bytes

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

@launchdarkly/js-client-sdk size report
This is the brotli compressed size of the ESM build.
Compressed size: 24212 bytes
Compressed size limit: 25000
Uncompressed size: 83755 bytes

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits March 9, 2026 18:08
…de hook)

Remove out-of-scope code that belongs to other SDK-1866 subtasks:
- Remove server-side TestHook (got-based)
- Remove REST-to-WebSocket adapter bridge + CLI
- Remove TestHarnessWebSocket implementation
- Remove ContractTestConfig type
- Remove tsconfig.server.json and tsconfig.adapter.json
- Remove got, ws, express, cors, body-parser, node-server-sdk dependencies
- Update package.json exports (only . and ./client remain)
- Update README to reflect scoped-down package

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…owser contract tests

- Replace local compat.ts types with imports from @launchdarkly/js-client-sdk-common
- Migrate browser contract test entity to import CommandParams, ConfigParams,
  makeLogger, and TestHook from @launchdarkly/js-contract-test-utils/client
- Delete duplicated local files from browser entity (CommandParams.ts,
  ConfigParams.ts, makeLogger.ts, TestHook.ts)
- Add @launchdarkly/js-contract-test-utils dependency to browser entity

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
@joker23

joker23 commented Mar 9, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
cursor[bot]

This comment was marked as resolved.

@joker23 joker23 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like we are only migrating the contract tests in browser... we should check if we can also migrate the contract tests in electron and react-native sdks as they are also based on client side sdks

Comment thread packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts
Comment thread packages/sdk/browser/contract-tests/entity/package.json Outdated
…deps, convert enums to const objects

- Fix dependency versions to use workspace:^ format in browser entity package.json
- Convert CommandType, ValueType, HookStage from enums to const objects with type unions (repo guideline)
- Consolidate HookStage definition in CommandParams.ts (remove duplicate from ConfigParams.ts)
- Migrate electron contract tests to use @launchdarkly/js-contract-test-utils/client
- Migrate react-native contract tests to use @launchdarkly/js-contract-test-utils/client
- Delete local CommandParams.ts, ConfigParams.ts, makeLogger.ts, TestHook.ts from electron and react-native

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Done — both electron and react-native contract tests are now migrated to import from @launchdarkly/js-contract-test-utils/client. Deleted the local CommandParams.ts, ConfigParams.ts, makeLogger.ts, and TestHook.ts from both.

Also converted the enums (CommandType, ValueType, HookStage) to as const objects with derived union types per repo guidelines, and fixed both dependency versions to workspace:^.

devin-ai-integration Bot and others added 5 commits March 9, 2026 19:31
…tests

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
- Delete local CommandParams.ts, ConfigParams.ts, makeLogger.ts, TestHook.ts
- Update ClientEntity.ts, ClientRoot.tsx, TestHarnessWebSocket.ts imports
- Add @launchdarkly/js-contract-test-utils dependency with workspace:^

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…compatibility

Next.js Turbopack resolves modules from TypeScript source directly and
cannot resolve .js extensions pointing to .ts files. Remove all .js
extensions from internal imports since the package is consumed as
TypeScript source.

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Metro bundler doesn't resolve package.json 'exports' subpaths by default.
Enable unstable_enablePackageExports so Metro can resolve
@launchdarkly/js-contract-test-utils/client subpath import.

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
@joker23 joker23 marked this pull request as ready for review March 9, 2026 21:17
@joker23 joker23 requested a review from a team as a code owner March 9, 2026 21:17
cursor[bot]

This comment was marked as resolved.

@kinyoklion kinyoklion changed the title feat: create @launchdarkly/js-contract-test-utils package (SDK-2008) feat: create @launchdarkly/js-contract-test-utils package Mar 10, 2026
* @param id - The unique identifier for the client.
* @param client - The client entity to store.
*/
add(id: string, client: T): void {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add just return an ID and internally call nextId?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — updated add() to call nextId() internally and return the assigned ID. The signature is now add(client: T): string instead of add(id: string, client: T): void. Since ClientPool isn't consumed by any migration yet, this is a safe API change.

…the ID

Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
@joker23 joker23 merged commit 2de5c27 into main Mar 10, 2026
44 checks passed
@joker23 joker23 deleted the devin/SDK-2008-1773078228 branch March 10, 2026 17:10
@cursor cursor Bot mentioned this pull request Mar 10, 2026
joker23 pushed a commit that referenced this pull request Mar 16, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>browser: 0.1.12</summary>

##
[0.1.12](browser-v0.1.11...browser-v0.1.12)
(2026-03-16)


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @launchdarkly/js-client-sdk bumped from 4.3.1 to 4.4.0
</details>

<details><summary>browser-telemetry: 1.0.28</summary>

##
[1.0.28](browser-telemetry-v1.0.27...browser-telemetry-v1.0.28)
(2026-03-16)


### Dependencies

* The following workspace dependencies were updated
  * devDependencies
    * @launchdarkly/js-client-sdk bumped from 4.3.1 to 4.4.0
</details>

<details><summary>jest: 1.0.7</summary>

##
[1.0.7](jest-v1.0.6...jest-v1.0.7)
(2026-03-16)


### Dependencies

* The following workspace dependencies were updated
  * dependencies
* @launchdarkly/react-native-client-sdk bumped from ~10.14.1 to ~10.15.0
</details>

<details><summary>js-client-sdk: 4.4.0</summary>

##
[4.4.0](js-client-sdk-v4.3.1...js-client-sdk-v4.4.0)
(2026-03-16)


### Features

* Add experimental FDv2 configuration (unused)
([#1169](#1169))
([c7130cc](c7130cc))
* create @launchdarkly/js-contract-test-utils package
([#1163](#1163))
([2de5c27](2de5c27))


### Bug Fixes

* Allow 0 status code to be handled by the streaming error filter.
([d96b46b](d96b46b))
* Improve error handling for FDv2 streaming
([d96b46b](d96b46b))


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @launchdarkly/js-client-sdk-common bumped from 1.21.0 to 1.22.0
</details>

<details><summary>js-client-sdk-common: 1.22.0</summary>

##
[1.22.0](js-client-sdk-common-v1.21.0...js-client-sdk-common-v1.22.0)
(2026-03-16)


### Features

* Add experimental FDv2 configuration (unused)
([#1169](#1169))
([c7130cc](c7130cc))
* Add explicit disableCache setting.
([6be89dd](6be89dd))
* Add FDv1 polling synchronizer for FDv2 fallback (SDK-1923)
([#1159](#1159))
([498216a](498216a))
* Add mode resolution table for FDv2.
([#1146](#1146))
([ab2436d](ab2436d))
* FDv2 Cache Initializer
([#1147](#1147))
([7d6299f](7d6299f))


### Bug Fixes

* Allow 0 status code to be handled by the streaming error filter.
([d96b46b](d96b46b))
* Fix the calculation of the basis parameter for FDv2 streaming. (Does
not affect FDv1).
([#1165](#1165))
([bbdd6c6](bbdd6c6))
* Improve error handling for FDv2 streaming
([d96b46b](d96b46b))
* Max cached context enforcement wasn't working for 0.
([6be89dd](6be89dd))
* rename FDv2 object kind from `flagEval` to `flag-eval`
([#1185](#1185))
([cd4b119](cd4b119))
</details>

<details><summary>react-native-client-sdk: 10.15.0</summary>

##
[10.15.0](react-native-client-sdk-v10.14.1...react-native-client-sdk-v10.15.0)
(2026-03-16)


### Features

* Add experimental FDv2 configuration (unused)
([#1169](#1169))
([c7130cc](c7130cc))
* create @launchdarkly/js-contract-test-utils package
([#1163](#1163))
([2de5c27](2de5c27))


### Dependencies

* The following workspace dependencies were updated
  * dependencies
    * @launchdarkly/js-client-sdk-common bumped from 1.21.0 to 1.22.0
</details>

<details><summary>server-sdk-ai: 0.16.6</summary>

##
[0.16.6](server-sdk-ai-v0.16.5...server-sdk-ai-v0.16.6)
(2026-03-16)


### Bug Fixes

* Make defaultValue optional with a disabled default
([#1144](#1144))
([e46769b](e46769b))
</details>

<details><summary>server-sdk-ai-langchain: 0.5.2</summary>

##
[0.5.2](server-sdk-ai-langchain-v0.5.1...server-sdk-ai-langchain-v0.5.2)
(2026-03-16)


### Dependencies

* The following workspace dependencies were updated
  * devDependencies
    * @launchdarkly/server-sdk-ai bumped from ^0.16.5 to ^0.16.6
  * peerDependencies
* @launchdarkly/server-sdk-ai bumped from ^0.15.0 || ^0.16.0 to ^0.16.6
</details>

<details><summary>server-sdk-ai-openai: 0.5.2</summary>

##
[0.5.2](server-sdk-ai-openai-v0.5.1...server-sdk-ai-openai-v0.5.2)
(2026-03-16)


### Dependencies

* The following workspace dependencies were updated
  * devDependencies
    * @launchdarkly/server-sdk-ai bumped from ^0.16.5 to ^0.16.6
  * peerDependencies
* @launchdarkly/server-sdk-ai bumped from ^0.15.0 || ^0.16.0 to ^0.16.6
</details>

<details><summary>server-sdk-ai-vercel: 0.5.2</summary>

##
[0.5.2](server-sdk-ai-vercel-v0.5.1...server-sdk-ai-vercel-v0.5.2)
(2026-03-16)


### Dependencies

* The following workspace dependencies were updated
  * devDependencies
    * @launchdarkly/server-sdk-ai bumped from ^0.16.5 to ^0.16.6
  * peerDependencies
* @launchdarkly/server-sdk-ai bumped from ^0.15.0 || ^0.16.0 to ^0.16.6
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Primarily automated release/version bumps and changelog updates across
multiple packages; no functional code changes beyond embedded SDK
version constants and dependency bumps.
> 
> **Overview**
> Publishes a new release across the monorepo by bumping versions in
`.release-please-manifest.json`, updating package `CHANGELOG.md`s, and
syncing `package.json` dependency ranges.
> 
> Notable bumps include `@launchdarkly/js-client-sdk` to `4.4.0` (and
`@launchdarkly/js-client-sdk-common` to `1.22.0`),
`@launchdarkly/react-native-client-sdk` to `10.15.0`,
`@launchdarkly/server-sdk-ai` to `0.16.6`, and corresponding
provider/aggregator packages (`server-sdk-ai-*`,
`@launchdarkly/browser`, `browser-telemetry`, `@launchdarkly/jest`).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
a6d9634. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions github-actions Bot mentioned this pull request Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants